/*
 * Syntelos 'SX'
 * Copyright (C) 1998, 2007  John Pritchard.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */
package syntelos.sx;

import alto.sys.Reference;

/**
 * <p> Client processor.  </p>
 * 
 * @author jdp
 * @since 1.6
 */
public final class Service
    extends SX.Daemon.Service
{

    /**
     * Called from {@link Server#init()}.
     */
    final static void Init(){
        int ThreadsLimit = syntelos.sys.statistic.Proc.DepthByCount();
        synchronized(Group.Service){
            int Counter = Group.Service.activeCount();
            while (Counter < ThreadsLimit){
                new Service(Counter++).start();
            }
        }
    }


    private static volatile int GCount = 0;
    private final static void GC(){
        if (0 == (GCount++)%50)
            java.lang.System.gc();
    }


    private Service(int Counter){
        super("Sys/SX/Service/"+Counter);
    }


    public void run(){
        try {
            while (true){
                Socket client = Socket.Queue.Waitfor();
                Server server = client.getServer();
                Request request = null;
                Response response = null;
                try {
                    client.handshake();
                    boolean persistent = true;
                    while (persistent){
                        client.connect();
                        try {
                            /*
                             * Read client input
                             */
                            client.lockReadEnter();
                            try {
                                request = new Request(client);
                                Accounting.Validate(request);
                                this.pushContext(request);
                            }
                            finally {
                                client.lockReadExit();
                            }
                            /*
                             * No client I/O, (response.init)
                             */
                            response = request.respond();
                            /*
                             * Write client output
                             */
                            client.lockWriteEnter();
                            try {
                                persistent = response.write();
                            }
                            finally {
                                client.lockWriteExit();
                            }
                        }
                        finally {
                            this.popContext();
                            if (null != response){
                                response.close();
                                Accounting.Complete(server,request,response);
                                request = null;
                                response = null;
                            }
                        }
                    }
                }
                catch (Ignore error){
                    if (null != response)
                        response.close();
                    request = null;
                    response = null;
                }
                catch (java.net.SocketTimeoutException error){
                    if (null != response)
                        response.close();
                    request = null;
                    response = null;
                }
                catch (java.io.IOException error){
                    try {
                        Accounting.Complete(server,request,response,error);
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (alto.sys.BadRequestException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.BadRequest().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (alto.sys.NotFoundException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.NotFound().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (alto.sys.UnauthorizedException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.Unauthorized().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (alto.sys.UnknownProtocolException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.UnknownProtocol().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (alto.sys.ConflictException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.Conflict().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (alto.sys.ServiceUnavailableException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.ServiceUnavailable().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (alto.sys.RequestEntityTooLargeException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.RequestEntityTooLarge().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (java.lang.SecurityException error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.Unauthorized().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (java.lang.OutOfMemoryError error){

                    java.lang.System.gc();
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.Error().cloneResponse();
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                catch (java.lang.Throwable error){
                    try {
                        if (null != client && (null == response || response.isNotCommitted())){
                            response = Response.Error().cloneResponse(error);
                            response.error(client,request);
                            response.write();
                            Accounting.Complete(server,request,response,error);
                        }
                        else {
                            Accounting.Complete(server,request,response,error);
                        }
                    }
                    catch (Ignore smellysock){
                    }
                    catch (Throwable neverdie){
                        neverdie.printStackTrace();
                    }
                    finally {
                        if (null != response)
                            response.close();
                        request = null;
                        response = null;
                    }
                }
                finally {
                    /*
                     * Close connection or release shm
                     */
                    client.release();

                    GC();
                }
            }
        }
        catch (InterruptedException inx){
            return;
        }
    }

}
